library(tidyverse)
library(patchwork)
library(emmeans)
library(simglm)
library(ggforce)

Simulate data

set.seed(3119) 

sim_arguments <- list(
  formula = y ~ 1 + hours + motivation + study + method,
  fixed = list(hours = list(var_type = 'ordinal', levels = 0:15),
               motivation = list(var_type = 'continuous', mean = 0, sd = 1),
               study = list(var_type = 'factor', 
                            levels = c('alone', 'others'),
                            prob = c(0.53, 0.47)),
               method = list(var_type = 'factor', 
                            levels = c('read', 'summarise', 'self-test'),
                            prob = c(0.3, 0.4, 0.3))),
  error = list(variance = 20),
  sample_size = 250,
  reg_weights = c(0.6, 1.4, 1.5, 6, 6, 2)
)

df3 <- simulate_fixed(data = NULL, sim_arguments) %>%
  simulate_error(sim_arguments) %>%
  generate_response(sim_arguments)

test_study3 <- df3 %>%
  dplyr::select(y, hours, motivation, study, method) %>%
  mutate(
    ID = paste("ID", 101:350, sep = ""),
    score = round(y+abs(min(y))),
    motivation = round(motivation, 2),
    study = factor(study),
    method = factor(method)
  ) %>%
  dplyr::select(ID, score, hours, motivation, study, method)

study (two levels)

Group means

test_study3 |>
  group_by(study) |>
  summarise(
    avg_score = round(mean(score), 2)
  )

mean_alone <- filter(test_study3, study == 'alone')$score |> mean()
mean_others <- filter(test_study3, study == 'others')$score |> mean()

Plot study data

test_study3 |>
  ggplot(aes(x = study, y = score, fill = study, colour = study)) +
  geom_violin(alpha = 0.5) +
  geom_sina(alpha = 0.5) +
  theme(legend.position = 'none') +
  stat_summary(colour = 'black', fun = mean, geom = 'point', size = 2) +
  geom_segment(aes(x = 'alone', xend = 'others', y = mean_alone, yend = mean_others), colour = 'black') +
  NULL

Plot on xy plane, dummy-coded.

contrasts(test_study3$study)
       others
alone       0
others      1

alone = ref level.

xlim_lower <- -2.2
xlim_upper <-  2.2
ylim_lower <- -25
ylim_upper <-  55

test_study3 |>
  mutate(study_num = ifelse(study == 'alone', 0, 1)) |>
  ggplot(aes(x = study_num, y = score, fill = study, colour = study)) +
  geom_jitter(alpha = 0.5, width = 0.1) +
  scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
  scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
  geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_alone, yend = mean_others), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_alone, yend = mean_alone), colour = 'red', linewidth = 2) +
  geom_segment(aes(x = 1, xend = 1, y = mean_alone, yend = mean_others), colour = 'red', linewidth = 2) +
  NULL

Add note that the manual stuff is so you understand how coding works. But you’ll never have to do it this laboriously.

Exact same outcome:

  • manual ifelse
  • contr.treatment()

Model score ~ study

mod1 <- lm(score ~ study, data = test_study3)
summary(mod1)

Call:
lm(formula = score ~ study, data = test_study3)

Residuals:
     Min       1Q   Median       3Q      Max 
-22.7902  -4.7902   0.2098   5.4766  18.2098 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  22.7902     0.6603   34.52  < 2e-16 ***
studyothers   4.7332     1.0093    4.69 4.53e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 7.896 on 248 degrees of freedom
Multiple R-squared:  0.08145,   Adjusted R-squared:  0.07775 
F-statistic: 21.99 on 1 and 248 DF,  p-value: 4.525e-06

t-test

t.test(score ~ study, var.equal = T, data = test_study3)

    Two Sample t-test

data:  score by study
t = -4.6895, df = 248, p-value = 4.525e-06
alternative hypothesis: true difference in means between group alone and group others is not equal to 0
95 percent confidence interval:
 -6.721058 -2.745251
sample estimates:
 mean in group alone mean in group others 
            22.79021             27.52336 

emmeans

(mod1_emm <- emmeans(mod1, ~study))
 study  emmean    SE  df lower.CL upper.CL
 alone    22.8 0.660 248     21.5     24.1
 others   27.5 0.763 248     26.0     29.0

Confidence level used: 0.95 
(p_mod1_emm <- mod1_emm |> plot() +
  coord_flip())

overlay emmeans on data plot

mod1_emm_tib <- mod1_emm |>
  as_tibble() |>
  rename(score = emmean)
test_study3 |>
  ggplot(aes(x = study, y = score, fill = study, colour = study)) +
  geom_violin(alpha = 0.5) +
  geom_jitter(alpha = 0.5, width = 0.2) +
  theme(legend.position = 'none') +
  geom_errorbar(data = mod1_emm_tib, aes(ymin = lower.CL, ymax = upper.CL), colour = 'black', width = 0.2) +
  geom_point(data = mod1_emm_tib, colour = 'black', size = 3) +
  NULL

pairwise comparisons

levels(test_study3$study)
[1] "alone"  "others"

For each comparison we want to make, assign 1 to the level we’re interested in, and assign -1 to the level you want to compare it to. If there are any other levels in there, assign them 0.

mod1_comparisons <- list(
  'alone vs. others' = c(
    1,   # alone
    -1   # others
  )
)

contrast(
  object = mod1_emm,
  method = mod1_comparisons
)
 contrast         estimate   SE  df t.ratio p.value
 alone vs. others    -4.73 1.01 248  -4.690  <.0001

Basically reconstructs the model’s estimates. Not so interesting.

Change ref level: coefs are diff but emmeans are same

Original contrasts:

contrasts(test_study3$study)
       others
alone       0
others      1

Now, let’s flip it:

contrasts(test_study3$study) <- contr.treatment(
  levels(test_study3$study), 
  base = 2)
contrasts(test_study3$study)
       alone
alone      1
others     0

plot flipped data

xlim_lower <- -2.2
xlim_upper <-  2.2
ylim_lower <- -25
ylim_upper <-  55

test_study3 |>
  mutate(study_num = ifelse(study == 'alone', 1, 0)) |>
  ggplot(aes(x = study_num, y = score, fill = study, colour = study)) +
  geom_jitter(alpha = 0.5, width = 0.1) +
  scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
  scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
  geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_others, yend = mean_alone), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_others, yend = mean_others), colour = 'red', linewidth = 2) +
  geom_segment(aes(x = 1, xend = 1, y = mean_others, yend = mean_alone), colour = 'red', linewidth = 2) +
  NULL

fit model (different)

mod1b <- lm(score ~ study, data = test_study3)
coef(mod1b)
(Intercept)  studyalone 
  27.523364   -4.733155 
  • What is intercept? (mean when study = others)
  • What is studyalone? (diff between levels: mean score when study = alone minus mean score when study = others)

Compare this to the original coefs from mod1:

coef(mod1)
(Intercept) studyothers 
  22.790210    4.733155 
  • What is intercept? (mean when study = alone)
  • What is studyothers? (diff between levels: mean score when study = others minus mean score when study = alone)

get emmeans (same)

(mod1b_emm <- emmeans(mod1b, ~study))
 study  emmean    SE  df lower.CL upper.CL
 alone    22.8 0.660 248     21.5     24.1
 others   27.5 0.763 248     26.0     29.0

Confidence level used: 0.95 
p_mod1b_emm <- mod1b_emm |> plot() +
  coord_flip() +
  ggtitle('With reference level = others')

p_mod1_emm <- p_mod1_emm + ggtitle('With reference level = alone')

p_mod1b_emm + p_mod1_emm

Identical!

method (three levels)

Group means

test_study3 |>
  group_by(method) |>
  summarise(
    avg_score = round(mean(score), 2)
  )

mean_read <- filter(test_study3, method == 'read')$score |> mean()
mean_self <- filter(test_study3, method == 'self-test')$score |> mean()
mean_summ <- filter(test_study3, method == 'summarise')$score |> mean()

Plot data

test_study3 |>
  ggplot(aes(x = method, y = score, fill = method, colour = method)) +
  geom_violin(alpha = 0.5) +
  geom_jitter(alpha = 0.5, width = 0.2) +
  theme(legend.position = 'none') +
  NULL

Goal: Compare the means of each group to one another. That was easy when we had only two groups: one straight line can go from mean to mean. But now one straight line won’t cut it anymore.

The smallest number of lines that will connect all the means = 2. Instead of estimating one line, we will estimate two.

And in general, if we have \(n\) groups, we will be estimating \(n-1\) lines.

The lines that we’re going to be estimating now:

test_study3 |>
  ggplot(aes(x = method, y = score, fill = method, colour = method)) +
  geom_violin(alpha = 0.5) +
  geom_jitter(alpha = 0.5, width = 0.2) +
  theme(legend.position = 'none') +
  stat_summary(colour = 'black', fun = mean, geom = 'point', size = 2) +
  # line from read to self-test:
  geom_segment(colour = 'black', aes(x = 'read', xend = 'self-test', y = mean_read, yend = mean_self)) +
  # line from read to summarise:
  geom_segment(colour = 'black', aes(x = 'read', xend = 'summarise', y = mean_read, yend = mean_summ)) +
  NULL

But I’m lying a little bit. This looks like going to go from 0 to 1 in one line, and 0 to 2 in the other line. But that’s not true – this is just the intuition about what groups we are comparing. Really what we’re going to do is this:

Plot dummy-coded method in xy space

Two panels: the first will be the difference between read and self-test (the first variable), the second will be difference between read and summarise (the second variable).

xlim_lower <- -2.2
xlim_upper <-  2.2
ylim_lower <- -25
ylim_upper <-  55

p1 <- test_study3 |> 
  filter(method %in% c('read', 'self-test')) |>
  mutate(method_num = ifelse(method == 'read', 0, 1)) |>
  ggplot(aes(x = method_num, y = score, fill = method, colour = method)) +
  geom_jitter(alpha = 0.5, width = 0.1) +
  scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
  scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
  geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_self), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_read), colour = 'red', linewidth = 2) +
  geom_segment(aes(x = 1, xend = 1, y = mean_read, yend = mean_self), colour = 'red', linewidth = 2) +
  labs(
    title = 'First line: read vs. self-test'
  ) +
  NULL

p2 <- test_study3 |> 
  filter(method %in% c('read', 'summarise')) |>
  mutate(method_num = ifelse(method == 'read', 0, 1)) |>
  ggplot(aes(x = method_num, y = score, fill = method, colour = method)) +
  geom_jitter(alpha = 0.5, width = 0.1) +
  scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
  scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
  geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_summ), colour = 'black') +
  geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_read), colour = 'red', linewidth = 2) +
  geom_segment(aes(x = 1, xend = 1, y = mean_read, yend = mean_summ), colour = 'red', linewidth = 2) +
  labs(
    title = 'Second line: read vs. summarise'
  ) +  
  NULL

p1 + p2

Dummy-code method variable

Manual dummy-coding:

dummyCoded <- test_study3 %>%
  select(ID, score, method) %>%
  mutate(
    method1 = ifelse(method == "self-test", 1, 0),
    method2 = ifelse(method == "summarise", 1, 0))

dummyCoded
contrasts(test_study3$method)
          self-test summarise
read              0         0
self-test         1         0
summarise         0         1

Model score ~ method

mod2 <- lm(score ~ method, data = dummyCoded)
summary(mod2)

Call:
lm(formula = score ~ method, data = dummyCoded)

Residuals:
     Min       1Q   Median       3Q      Max 
-23.4138  -5.3593  -0.1959   5.7496  17.8041 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)      23.4138     0.8662  27.031   <2e-16 ***
methodself-test   4.1620     1.3188   3.156   0.0018 ** 
methodsummarise   0.7821     1.1930   0.656   0.5127    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.079 on 247 degrees of freedom
Multiple R-squared:  0.04224,   Adjusted R-squared:  0.03448 
F-statistic: 5.447 on 2 and 247 DF,  p-value: 0.004845

t-tests

(Nearly) equivalent t-tests (different bc diff degrees of freedom, bc filtering dataset. but close).

dC1 <- test_study3 |> 
  filter(method %in% c('read', 'self-test')) |>
  mutate(method = factor(method, levels = c('read', 'self-test')))
contrasts(dC1$method) <- contr.treatment(2)
contrasts(dC1$method)
          2
read      0
self-test 1
t.test(score ~ method, var.equal = T, data = dC1)

    Two Sample t-test

data:  score by method
t = -3.1354, df = 151, p-value = 0.002063
alternative hypothesis: true difference in means between group read and group self-test is not equal to 0
95 percent confidence interval:
 -6.784657 -1.539272
sample estimates:
     mean in group read mean in group self-test 
               23.41379                27.57576 
dC2 <- test_study3 |> 
  filter(method %in% c('read', 'summarise')) |>
  mutate(method = factor(method, levels = c('read', 'summarise')))
contrasts(dC2$method) <- contr.treatment(2)
contrasts(dC2$method)
          2
read      0
summarise 1
t.test(score ~ method, var.equal = T, data = dC2)

    Two Sample t-test

data:  score by method
t = -0.66342, df = 182, p-value = 0.5079
alternative hypothesis: true difference in means between group read and group summarise is not equal to 0
95 percent confidence interval:
 -3.108082  1.543915
sample estimates:
     mean in group read mean in group summarise 
               23.41379                24.19588 

emmeans

Before, I said that the smallest number of lines we need to compare three groups is two. But this actually means that one of the possible comparisons is left out.

(??? above the missing line between self-test and summarise)

Options:

  • make something else ref level. Then we get that comparison. But then we’re missing a different third comparison.
  • get emmeans, and once we’re in the outcome space, we can compute differences between whatever we want! yay!
(mod2_emm <- emmeans(mod2, ~method))
 method    emmean    SE  df lower.CL upper.CL
 read        23.4 0.866 247     21.7     25.1
 self-test   27.6 0.994 247     25.6     29.5
 summarise   24.2 0.820 247     22.6     25.8

Confidence level used: 0.95 
mod2_emm |> plot() +
  coord_flip()

overlay emmeans on data plot

mod2_emm_tib <- mod2_emm |>
  as_tibble() |>
  rename(score = emmean)
test_study3 |>
  ggplot(aes(x = method, y = score, fill = method, colour = method)) +
  geom_violin(alpha = 0.5) +
  geom_jitter(alpha = 0.5, width = 0.2) +
  theme(legend.position = 'none') +
  geom_errorbar(data = mod2_emm_tib, aes(ymin = lower.CL, ymax = upper.CL), colour = 'black', width = 0.2) +
  geom_point(data = mod2_emm_tib, colour = 'black', size = 3) +
  NULL

pairwise comparisons

levels(test_study3$method)
[1] "read"      "self-test" "summarise"

Assign 1 to the level we are interested in, and -1 to the baseline we want to compare it to.

mod2_comparisons <- list(
  'Self-test vs. Read' = c(-1, 1, 0),  # read self-test summarise, in that order
  'Summarise vs. Read' = c(-1, 0, 1),
  'Self-test vs. Summarise' = c(0, 1, -1),
  'mean(Self-test, summarise) vs. Read' = c(-1, 0.5, 0.5)  # weights must sum to 0 -- pools self-test and summarise tog and pits that mean against mean of Read
)

1 - 1

mean(Self-test) - mean(Read) = positive, bc self-test is larger than read

(mod2_comparisons_test <- contrast(mod2_emm, mod2_comparisons))
 contrast                            estimate   SE  df t.ratio p.value
 Self-test vs. Read                     4.162 1.32 247   3.156  0.0018
 Summarise vs. Read                     0.782 1.19 247   0.656  0.5127
 Self-test vs. Summarise                3.380 1.29 247   2.622  0.0093
 mean(Self-test, summarise) vs. Read    2.472 1.08 247   2.290  0.0229

contrast() gives us the p-values for each difference. We can also get the 95% CIs of each difference by giving the outcome of contrast() into confint().

confint(mod2_comparisons_test)
 contrast                            estimate   SE  df lower.CL upper.CL
 Self-test vs. Read                     4.162 1.32 247    1.564     6.76
 Summarise vs. Read                     0.782 1.19 247   -1.568     3.13
 Self-test vs. Summarise                3.380 1.29 247    0.841     5.92
 mean(Self-test, summarise) vs. Read    2.472 1.08 247    0.345     4.60

Confidence level used: 0.95 
LS0tCnRpdGxlOiAiRVAgcGxheWdyb3VuZCDigJMgMDYgY2F0ZWcgZHVtbXkiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQotLS0KCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZW1tZWFucykKbGlicmFyeShzaW1nbG0pCmBgYAoKCiMgU2ltdWxhdGUgZGF0YQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc2V0LnNlZWQoMzExOSkgCgpzaW1fYXJndW1lbnRzIDwtIGxpc3QoCiAgZm9ybXVsYSA9IHkgfiAxICsgaG91cnMgKyBtb3RpdmF0aW9uICsgc3R1ZHkgKyBtZXRob2QsCiAgZml4ZWQgPSBsaXN0KGhvdXJzID0gbGlzdCh2YXJfdHlwZSA9ICdvcmRpbmFsJywgbGV2ZWxzID0gMDoxNSksCiAgICAgICAgICAgICAgIG1vdGl2YXRpb24gPSBsaXN0KHZhcl90eXBlID0gJ2NvbnRpbnVvdXMnLCBtZWFuID0gMCwgc2QgPSAxKSwKICAgICAgICAgICAgICAgc3R1ZHkgPSBsaXN0KHZhcl90eXBlID0gJ2ZhY3RvcicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygnYWxvbmUnLCAnb3RoZXJzJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjUzLCAwLjQ3KSksCiAgICAgICAgICAgICAgIG1ldGhvZCA9IGxpc3QodmFyX3R5cGUgPSAnZmFjdG9yJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdyZWFkJywgJ3N1bW1hcmlzZScsICdzZWxmLXRlc3QnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2IgPSBjKDAuMywgMC40LCAwLjMpKSksCiAgZXJyb3IgPSBsaXN0KHZhcmlhbmNlID0gMjApLAogIHNhbXBsZV9zaXplID0gMjUwLAogIHJlZ193ZWlnaHRzID0gYygwLjYsIDEuNCwgMS41LCA2LCA2LCAyKQopCgpkZjMgPC0gc2ltdWxhdGVfZml4ZWQoZGF0YSA9IE5VTEwsIHNpbV9hcmd1bWVudHMpICU+JQogIHNpbXVsYXRlX2Vycm9yKHNpbV9hcmd1bWVudHMpICU+JQogIGdlbmVyYXRlX3Jlc3BvbnNlKHNpbV9hcmd1bWVudHMpCgp0ZXN0X3N0dWR5MyA8LSBkZjMgJT4lCiAgZHBseXI6OnNlbGVjdCh5LCBob3VycywgbW90aXZhdGlvbiwgc3R1ZHksIG1ldGhvZCkgJT4lCiAgbXV0YXRlKAogICAgSUQgPSBwYXN0ZSgiSUQiLCAxMDE6MzUwLCBzZXAgPSAiIiksCiAgICBzY29yZSA9IHJvdW5kKHkrYWJzKG1pbih5KSkpLAogICAgbW90aXZhdGlvbiA9IHJvdW5kKG1vdGl2YXRpb24sIDIpLAogICAgc3R1ZHkgPSBmYWN0b3Ioc3R1ZHkpLAogICAgbWV0aG9kID0gZmFjdG9yKG1ldGhvZCkKICApICU+JQogIGRwbHlyOjpzZWxlY3QoSUQsIHNjb3JlLCBob3VycywgbW90aXZhdGlvbiwgc3R1ZHksIG1ldGhvZCkKCmBgYAoKCiMgYHN0dWR5YCAodHdvIGxldmVscykKCiMjIEdyb3VwIG1lYW5zCgpgYGB7cn0KdGVzdF9zdHVkeTMgfD4KICBncm91cF9ieShzdHVkeSkgfD4KICBzdW1tYXJpc2UoCiAgICBhdmdfc2NvcmUgPSByb3VuZChtZWFuKHNjb3JlKSwgMikKICApCgptZWFuX2Fsb25lIDwtIGZpbHRlcih0ZXN0X3N0dWR5Mywgc3R1ZHkgPT0gJ2Fsb25lJykkc2NvcmUgfD4gbWVhbigpCm1lYW5fb3RoZXJzIDwtIGZpbHRlcih0ZXN0X3N0dWR5Mywgc3R1ZHkgPT0gJ290aGVycycpJHNjb3JlIHw+IG1lYW4oKQpgYGAKCgojIyBQbG90IGBzdHVkeWAgZGF0YQoKYGBge3J9CnRlc3Rfc3R1ZHkzIHw+CiAgZ2dwbG90KGFlcyh4ID0gc3R1ZHksIHkgPSBzY29yZSwgZmlsbCA9IHN0dWR5LCBjb2xvdXIgPSBzdHVkeSkpICsKICBnZW9tX3Zpb2xpbihhbHBoYSA9IDAuNSkgKwogIGdlb21fc2luYShhbHBoYSA9IDAuNSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKwogIHN0YXRfc3VtbWFyeShjb2xvdXIgPSAnYmxhY2snLCBmdW4gPSBtZWFuLCBnZW9tID0gJ3BvaW50Jywgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAnYWxvbmUnLCB4ZW5kID0gJ290aGVycycsIHkgPSBtZWFuX2Fsb25lLCB5ZW5kID0gbWVhbl9vdGhlcnMpLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgTlVMTApgYGAKClBsb3Qgb24geHkgcGxhbmUsIGR1bW15LWNvZGVkLgoKYGBge3J9CmNvbnRyYXN0cyh0ZXN0X3N0dWR5MyRzdHVkeSkKYGBgCgoKYGFsb25lYCA9IHJlZiBsZXZlbC4KCmBgYHtyfQp4bGltX2xvd2VyIDwtIC0yLjIKeGxpbV91cHBlciA8LSAgMi4yCnlsaW1fbG93ZXIgPC0gLTI1CnlsaW1fdXBwZXIgPC0gIDU1Cgp0ZXN0X3N0dWR5MyB8PgogIG11dGF0ZShzdHVkeV9udW0gPSBpZmVsc2Uoc3R1ZHkgPT0gJ2Fsb25lJywgMCwgMSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gc3R1ZHlfbnVtLCB5ID0gc2NvcmUsIGZpbGwgPSBzdHVkeSwgY29sb3VyID0gc3R1ZHkpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoeGxpbV9sb3dlciwgeGxpbV91cHBlciksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyh5bGltX2xvd2VyLCB5bGltX3VwcGVyKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHhsaW1fbG93ZXIsIHhlbmQgPSB4bGltX3VwcGVyLCB5ID0gMCwgeWVuZCA9IDApLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAwLCB5ID0geWxpbV9sb3dlciwgeWVuZCA9IHlsaW1fdXBwZXIpLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9hbG9uZSwgeWVuZCA9IG1lYW5fb3RoZXJzKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9hbG9uZSwgeWVuZCA9IG1lYW5fYWxvbmUpLCBjb2xvdXIgPSAncmVkJywgbGluZXdpZHRoID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEsIHhlbmQgPSAxLCB5ID0gbWVhbl9hbG9uZSwgeWVuZCA9IG1lYW5fb3RoZXJzKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBOVUxMCmBgYAoKCkFkZCBub3RlIHRoYXQgdGhlIG1hbnVhbCBzdHVmZiBpcyBzbyB5b3UgdW5kZXJzdGFuZCBob3cgY29kaW5nIHdvcmtzLgpCdXQgeW91J2xsIG5ldmVyIGhhdmUgdG8gZG8gaXQgdGhpcyBsYWJvcmlvdXNseS4KCkV4YWN0IHNhbWUgb3V0Y29tZToKCi0gbWFudWFsIGlmZWxzZQotIGBjb250ci50cmVhdG1lbnQoKWAKCgoKCiMjIE1vZGVsIGBzY29yZSB+IHN0dWR5YAoKYGBge3J9Cm1vZDEgPC0gbG0oc2NvcmUgfiBzdHVkeSwgZGF0YSA9IHRlc3Rfc3R1ZHkzKQpzdW1tYXJ5KG1vZDEpCmBgYAoKIyMjIHQtdGVzdAoKYGBge3J9CnQudGVzdChzY29yZSB+IHN0dWR5LCB2YXIuZXF1YWwgPSBULCBkYXRhID0gdGVzdF9zdHVkeTMpCmBgYAoKIyMgZW1tZWFucwoKYGBge3J9Cihtb2QxX2VtbSA8LSBlbW1lYW5zKG1vZDEsIH5zdHVkeSkpCmBgYAoKCmBgYHtyfQoocF9tb2QxX2VtbSA8LSBtb2QxX2VtbSB8PiBwbG90KCkgKwogIGNvb3JkX2ZsaXAoKSkKYGBgCgojIyMgb3ZlcmxheSBlbW1lYW5zIG9uIGRhdGEgcGxvdAoKYGBge3J9Cm1vZDFfZW1tX3RpYiA8LSBtb2QxX2VtbSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgcmVuYW1lKHNjb3JlID0gZW1tZWFuKQpgYGAKCmBgYHtyfQp0ZXN0X3N0dWR5MyB8PgogIGdncGxvdChhZXMoeCA9IHN0dWR5LCB5ID0gc2NvcmUsIGZpbGwgPSBzdHVkeSwgY29sb3VyID0gc3R1ZHkpKSArCiAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjUpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBtb2QxX2VtbV90aWIsIGFlcyh5bWluID0gbG93ZXIuQ0wsIHltYXggPSB1cHBlci5DTCksIGNvbG91ciA9ICdibGFjaycsIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbW9kMV9lbW1fdGliLCBjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMykgKwogIE5VTEwKYGBgCgoKIyMjIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpgYGB7cn0KbGV2ZWxzKHRlc3Rfc3R1ZHkzJHN0dWR5KQpgYGAKCkZvciBlYWNoIGNvbXBhcmlzb24gd2Ugd2FudCB0byBtYWtlLCBhc3NpZ24gMSB0byB0aGUgbGV2ZWwgd2UncmUgaW50ZXJlc3RlZCBpbiwgYW5kIGFzc2lnbiAtMSB0byB0aGUgbGV2ZWwgeW91IHdhbnQgdG8gY29tcGFyZSBpdCB0by4KSWYgdGhlcmUgYXJlIGFueSBvdGhlciBsZXZlbHMgaW4gdGhlcmUsIGFzc2lnbiB0aGVtIDAuCgpgYGB7cn0KbW9kMV9jb21wYXJpc29ucyA8LSBsaXN0KAogICdhbG9uZSB2cy4gb3RoZXJzJyA9IGMoCiAgICAxLCAgICMgYWxvbmUKICAgIC0xICAgIyBvdGhlcnMKICApCikKCmNvbnRyYXN0KAogIG9iamVjdCA9IG1vZDFfZW1tLAogIG1ldGhvZCA9IG1vZDFfY29tcGFyaXNvbnMKKQpgYGAKCkJhc2ljYWxseSByZWNvbnN0cnVjdHMgdGhlIG1vZGVsJ3MgZXN0aW1hdGVzLgpOb3Qgc28gaW50ZXJlc3RpbmcuCgoKIyMgQ2hhbmdlIHJlZiBsZXZlbDogY29lZnMgYXJlIGRpZmYgYnV0IGVtbWVhbnMgYXJlIHNhbWUKCk9yaWdpbmFsIGNvbnRyYXN0czoKCmBgYHtyfQpjb250cmFzdHModGVzdF9zdHVkeTMkc3R1ZHkpCmBgYAoKTm93LCBsZXQncyBmbGlwIGl0OgoKYGBge3J9CmNvbnRyYXN0cyh0ZXN0X3N0dWR5MyRzdHVkeSkgPC0gY29udHIudHJlYXRtZW50KAogIGxldmVscyh0ZXN0X3N0dWR5MyRzdHVkeSksIAogIGJhc2UgPSAyKQpjb250cmFzdHModGVzdF9zdHVkeTMkc3R1ZHkpCmBgYAoKIyMjIHBsb3QgZmxpcHBlZCBkYXRhCgpgYGB7cn0KeGxpbV9sb3dlciA8LSAtMi4yCnhsaW1fdXBwZXIgPC0gIDIuMgp5bGltX2xvd2VyIDwtIC0yNQp5bGltX3VwcGVyIDwtICA1NQoKdGVzdF9zdHVkeTMgfD4KICBtdXRhdGUoc3R1ZHlfbnVtID0gaWZlbHNlKHN0dWR5ID09ICdhbG9uZScsIDEsIDApKSB8PgogIGdncGxvdChhZXMoeCA9IHN0dWR5X251bSwgeSA9IHNjb3JlLCBmaWxsID0gc3R1ZHksIGNvbG91ciA9IHN0dWR5KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC41LCB3aWR0aCA9IDAuMSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKHhsaW1fbG93ZXIsIHhsaW1fdXBwZXIpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoeWxpbV9sb3dlciwgeWxpbV91cHBlciksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4bGltX2xvd2VyLCB4ZW5kID0geGxpbV91cHBlciwgeSA9IDAsIHllbmQgPSAwKSwgYXJyb3cgPSBhcnJvdyhlbmRzID0gJ2JvdGgnLCBsZW5ndGggPSB1bml0KDEyLCAncHQnKSksIGNvbG91ciA9ICdibGFjaycpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMCwgeSA9IHlsaW1fbG93ZXIsIHllbmQgPSB5bGltX3VwcGVyKSwgYXJyb3cgPSBhcnJvdyhlbmRzID0gJ2JvdGgnLCBsZW5ndGggPSB1bml0KDEyLCAncHQnKSksIGNvbG91ciA9ICdibGFjaycpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMSwgeSA9IG1lYW5fb3RoZXJzLCB5ZW5kID0gbWVhbl9hbG9uZSksIGNvbG91ciA9ICdibGFjaycpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMSwgeSA9IG1lYW5fb3RoZXJzLCB5ZW5kID0gbWVhbl9vdGhlcnMpLCBjb2xvdXIgPSAncmVkJywgbGluZXdpZHRoID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEsIHhlbmQgPSAxLCB5ID0gbWVhbl9vdGhlcnMsIHllbmQgPSBtZWFuX2Fsb25lKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBOVUxMCmBgYAoKIyMjIGZpdCBtb2RlbCAoZGlmZmVyZW50KQoKYGBge3J9Cm1vZDFiIDwtIGxtKHNjb3JlIH4gc3R1ZHksIGRhdGEgPSB0ZXN0X3N0dWR5MykKY29lZihtb2QxYikKYGBgCgotIFdoYXQgaXMgaW50ZXJjZXB0PyAobWVhbiB3aGVuIHN0dWR5ID0gb3RoZXJzKQotIFdoYXQgaXMgc3R1ZHlhbG9uZT8gKGRpZmYgYmV0d2VlbiBsZXZlbHM6IG1lYW4gc2NvcmUgd2hlbiBzdHVkeSA9IGFsb25lIG1pbnVzIG1lYW4gc2NvcmUgd2hlbiBzdHVkeSA9IG90aGVycykKCkNvbXBhcmUgdGhpcyB0byB0aGUgb3JpZ2luYWwgY29lZnMgZnJvbSBgbW9kMWA6CgpgYGB7cn0KY29lZihtb2QxKQpgYGAKCi0gV2hhdCBpcyBpbnRlcmNlcHQ/IChtZWFuIHdoZW4gc3R1ZHkgPSBhbG9uZSkKLSBXaGF0IGlzIHN0dWR5b3RoZXJzPyAoZGlmZiBiZXR3ZWVuIGxldmVsczogbWVhbiBzY29yZSB3aGVuIHN0dWR5ID0gb3RoZXJzIG1pbnVzIG1lYW4gc2NvcmUgd2hlbiBzdHVkeSA9IGFsb25lKQoKCiMjIyBnZXQgZW1tZWFucyAoc2FtZSkKCmBgYHtyfQoobW9kMWJfZW1tIDwtIGVtbWVhbnMobW9kMWIsIH5zdHVkeSkpCmBgYAoKYGBge3J9CnBfbW9kMWJfZW1tIDwtIG1vZDFiX2VtbSB8PiBwbG90KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgnV2l0aCByZWZlcmVuY2UgbGV2ZWwgPSBvdGhlcnMnKQoKcF9tb2QxX2VtbSA8LSBwX21vZDFfZW1tICsgZ2d0aXRsZSgnV2l0aCByZWZlcmVuY2UgbGV2ZWwgPSBhbG9uZScpCgpwX21vZDFiX2VtbSArIHBfbW9kMV9lbW0KYGBgCgpJZGVudGljYWwhCgoKIyBgbWV0aG9kYCAodGhyZWUgbGV2ZWxzKQoKIyMgR3JvdXAgbWVhbnMKCmBgYHtyfQp0ZXN0X3N0dWR5MyB8PgogIGdyb3VwX2J5KG1ldGhvZCkgfD4KICBzdW1tYXJpc2UoCiAgICBhdmdfc2NvcmUgPSByb3VuZChtZWFuKHNjb3JlKSwgMikKICApCgptZWFuX3JlYWQgPC0gZmlsdGVyKHRlc3Rfc3R1ZHkzLCBtZXRob2QgPT0gJ3JlYWQnKSRzY29yZSB8PiBtZWFuKCkKbWVhbl9zZWxmIDwtIGZpbHRlcih0ZXN0X3N0dWR5MywgbWV0aG9kID09ICdzZWxmLXRlc3QnKSRzY29yZSB8PiBtZWFuKCkKbWVhbl9zdW1tIDwtIGZpbHRlcih0ZXN0X3N0dWR5MywgbWV0aG9kID09ICdzdW1tYXJpc2UnKSRzY29yZSB8PiBtZWFuKCkKYGBgCgoKIyMgUGxvdCBkYXRhCgpgYGB7cn0KdGVzdF9zdHVkeTMgfD4KICBnZ3Bsb3QoYWVzKHggPSBtZXRob2QsIHkgPSBzY29yZSwgZmlsbCA9IG1ldGhvZCwgY29sb3VyID0gbWV0aG9kKSkgKwogIGdlb21fdmlvbGluKGFscGhhID0gMC41KSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4yKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgTlVMTApgYGAKCkdvYWw6IENvbXBhcmUgdGhlIG1lYW5zIG9mIGVhY2ggZ3JvdXAgdG8gb25lIGFub3RoZXIuClRoYXQgd2FzIGVhc3kgd2hlbiB3ZSBoYWQgb25seSB0d28gZ3JvdXBzOiBvbmUgc3RyYWlnaHQgbGluZSBjYW4gZ28gZnJvbSBtZWFuIHRvIG1lYW4uCkJ1dCBub3cgb25lIHN0cmFpZ2h0IGxpbmUgd29uJ3QgY3V0IGl0IGFueW1vcmUuCgpUaGUgc21hbGxlc3QgbnVtYmVyIG9mIGxpbmVzIHRoYXQgd2lsbCBjb25uZWN0IGFsbCB0aGUgbWVhbnMgPSAyLgpJbnN0ZWFkIG9mIGVzdGltYXRpbmcgb25lIGxpbmUsIHdlIHdpbGwgZXN0aW1hdGUgdHdvLgoKQW5kIGluIGdlbmVyYWwsIGlmIHdlIGhhdmUgJG4kIGdyb3Vwcywgd2Ugd2lsbCBiZSBlc3RpbWF0aW5nICRuLTEkIGxpbmVzLgoKVGhlIGxpbmVzIHRoYXQgd2UncmUgZ29pbmcgdG8gYmUgZXN0aW1hdGluZyBub3c6CgpgYGB7cn0KdGVzdF9zdHVkeTMgfD4KICBnZ3Bsb3QoYWVzKHggPSBtZXRob2QsIHkgPSBzY29yZSwgZmlsbCA9IG1ldGhvZCwgY29sb3VyID0gbWV0aG9kKSkgKwogIGdlb21fdmlvbGluKGFscGhhID0gMC41KSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4yKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgc3RhdF9zdW1tYXJ5KGNvbG91ciA9ICdibGFjaycsIGZ1biA9IG1lYW4sIGdlb20gPSAncG9pbnQnLCBzaXplID0gMikgKwogICMgbGluZSBmcm9tIHJlYWQgdG8gc2VsZi10ZXN0OgogIGdlb21fc2VnbWVudChjb2xvdXIgPSAnYmxhY2snLCBhZXMoeCA9ICdyZWFkJywgeGVuZCA9ICdzZWxmLXRlc3QnLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zZWxmKSkgKwogICMgbGluZSBmcm9tIHJlYWQgdG8gc3VtbWFyaXNlOgogIGdlb21fc2VnbWVudChjb2xvdXIgPSAnYmxhY2snLCBhZXMoeCA9ICdyZWFkJywgeGVuZCA9ICdzdW1tYXJpc2UnLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zdW1tKSkgKwogIE5VTEwKYGBgCgoKQnV0IEknbSBseWluZyBhIGxpdHRsZSBiaXQuClRoaXMgbG9va3MgbGlrZSBnb2luZyB0byBnbyBmcm9tIDAgdG8gMSBpbiBvbmUgbGluZSwgYW5kIDAgdG8gMiBpbiB0aGUgb3RoZXIgbGluZS4KQnV0IHRoYXQncyBub3QgdHJ1ZSAtLSB0aGlzIGlzIGp1c3QgdGhlIGludHVpdGlvbiBhYm91dCB3aGF0IGdyb3VwcyB3ZSBhcmUgY29tcGFyaW5nLgpSZWFsbHkgd2hhdCB3ZSdyZSBnb2luZyB0byBkbyBpcyB0aGlzOgoKCiMjIFBsb3QgZHVtbXktY29kZWQgYG1ldGhvZGAgaW4geHkgc3BhY2UKClR3byBwYW5lbHM6IHRoZSBmaXJzdCB3aWxsIGJlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYHJlYWRgIGFuZCBgc2VsZi10ZXN0YCAodGhlIGZpcnN0IHZhcmlhYmxlKSwgdGhlIHNlY29uZCB3aWxsIGJlIGRpZmZlcmVuY2UgYmV0d2VlbiBgcmVhZGAgYW5kIGBzdW1tYXJpc2VgICh0aGUgc2Vjb25kIHZhcmlhYmxlKS4KCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNH0KeGxpbV9sb3dlciA8LSAtMi4yCnhsaW1fdXBwZXIgPC0gIDIuMgp5bGltX2xvd2VyIDwtIC0yNQp5bGltX3VwcGVyIDwtICA1NQoKcDEgPC0gdGVzdF9zdHVkeTMgfD4gCiAgZmlsdGVyKG1ldGhvZCAlaW4lIGMoJ3JlYWQnLCAnc2VsZi10ZXN0JykpIHw+CiAgbXV0YXRlKG1ldGhvZF9udW0gPSBpZmVsc2UobWV0aG9kID09ICdyZWFkJywgMCwgMSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gbWV0aG9kX251bSwgeSA9IHNjb3JlLCBmaWxsID0gbWV0aG9kLCBjb2xvdXIgPSBtZXRob2QpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoeGxpbV9sb3dlciwgeGxpbV91cHBlciksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyh5bGltX2xvd2VyLCB5bGltX3VwcGVyKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHhsaW1fbG93ZXIsIHhlbmQgPSB4bGltX3VwcGVyLCB5ID0gMCwgeWVuZCA9IDApLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAwLCB5ID0geWxpbV9sb3dlciwgeWVuZCA9IHlsaW1fdXBwZXIpLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zZWxmKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9yZWFkKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxLCB4ZW5kID0gMSwgeSA9IG1lYW5fcmVhZCwgeWVuZCA9IG1lYW5fc2VsZiksIGNvbG91ciA9ICdyZWQnLCBsaW5ld2lkdGggPSAyKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0ZpcnN0IGxpbmU6IHNlbGYtdGVzdCB2cy4gcmVhZCcKICApICsKICBOVUxMCgpwMiA8LSB0ZXN0X3N0dWR5MyB8PiAKICBmaWx0ZXIobWV0aG9kICVpbiUgYygncmVhZCcsICdzdW1tYXJpc2UnKSkgfD4KICBtdXRhdGUobWV0aG9kX251bSA9IGlmZWxzZShtZXRob2QgPT0gJ3JlYWQnLCAwLCAxKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBtZXRob2RfbnVtLCB5ID0gc2NvcmUsIGZpbGwgPSBtZXRob2QsIGNvbG91ciA9IG1ldGhvZCkpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYyh4bGltX2xvd2VyLCB4bGltX3VwcGVyKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKHlsaW1fbG93ZXIsIHlsaW1fdXBwZXIpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geGxpbV9sb3dlciwgeGVuZCA9IHhsaW1fdXBwZXIsIHkgPSAwLCB5ZW5kID0gMCksIGFycm93ID0gYXJyb3coZW5kcyA9ICdib3RoJywgbGVuZ3RoID0gdW5pdCgxMiwgJ3B0JykpLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeGVuZCA9IDAsIHkgPSB5bGltX2xvd2VyLCB5ZW5kID0geWxpbV91cHBlciksIGFycm93ID0gYXJyb3coZW5kcyA9ICdib3RoJywgbGVuZ3RoID0gdW5pdCgxMiwgJ3B0JykpLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeGVuZCA9IDEsIHkgPSBtZWFuX3JlYWQsIHllbmQgPSBtZWFuX3N1bW0pLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeGVuZCA9IDEsIHkgPSBtZWFuX3JlYWQsIHllbmQgPSBtZWFuX3JlYWQpLCBjb2xvdXIgPSAncmVkJywgbGluZXdpZHRoID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEsIHhlbmQgPSAxLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zdW1tKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBsYWJzKAogICAgdGl0bGUgPSAnU2Vjb25kIGxpbmU6IHN1bW1hcmlzZSB2cy4gcmVhZCcKICApICsgIAogIE5VTEwKCnAxICsgcDIKYGBgCgoKIyMgRHVtbXktY29kZSBgbWV0aG9kYCB2YXJpYWJsZQoKTWFudWFsIGR1bW15LWNvZGluZzoKCmBgYHtyfQpkdW1teUNvZGVkIDwtIHRlc3Rfc3R1ZHkzICU+JQogIHNlbGVjdChJRCwgc2NvcmUsIG1ldGhvZCkgJT4lCiAgbXV0YXRlKAogICAgbWV0aG9kMSA9IGlmZWxzZShtZXRob2QgPT0gInNlbGYtdGVzdCIsIDEsIDApLAogICAgbWV0aG9kMiA9IGlmZWxzZShtZXRob2QgPT0gInN1bW1hcmlzZSIsIDEsIDApKQoKZHVtbXlDb2RlZApgYGAKCgpgYGB7cn0KY29udHJhc3RzKHRlc3Rfc3R1ZHkzJG1ldGhvZCkKYGBgCgoKCgojIyBNb2RlbCBgc2NvcmUgfiBtZXRob2RgCgpgYGB7cn0KbW9kMiA8LSBsbShzY29yZSB+IG1ldGhvZCwgZGF0YSA9IGR1bW15Q29kZWQpCnN1bW1hcnkobW9kMikKYGBgCgoKIyMjIHQtdGVzdHMKCihOZWFybHkpIGVxdWl2YWxlbnQgdC10ZXN0cyAoZGlmZmVyZW50IGJjIGRpZmYgZGVncmVlcyBvZiBmcmVlZG9tLCBiYyBmaWx0ZXJpbmcgZGF0YXNldC4gYnV0IGNsb3NlKS4KCmBgYHtyfQpkQzEgPC0gdGVzdF9zdHVkeTMgfD4gCiAgZmlsdGVyKG1ldGhvZCAlaW4lIGMoJ3JlYWQnLCAnc2VsZi10ZXN0JykpIHw+CiAgbXV0YXRlKG1ldGhvZCA9IGZhY3RvcihtZXRob2QsIGxldmVscyA9IGMoJ3JlYWQnLCAnc2VsZi10ZXN0JykpKQpjb250cmFzdHMoZEMxJG1ldGhvZCkgPC0gY29udHIudHJlYXRtZW50KDIpCmNvbnRyYXN0cyhkQzEkbWV0aG9kKQoKdC50ZXN0KHNjb3JlIH4gbWV0aG9kLCB2YXIuZXF1YWwgPSBULCBkYXRhID0gZEMxKQpgYGAKCgpgYGB7cn0KZEMyIDwtIHRlc3Rfc3R1ZHkzIHw+IAogIGZpbHRlcihtZXRob2QgJWluJSBjKCdyZWFkJywgJ3N1bW1hcmlzZScpKSB8PgogIG11dGF0ZShtZXRob2QgPSBmYWN0b3IobWV0aG9kLCBsZXZlbHMgPSBjKCdyZWFkJywgJ3N1bW1hcmlzZScpKSkKY29udHJhc3RzKGRDMiRtZXRob2QpIDwtIGNvbnRyLnRyZWF0bWVudCgyKQpjb250cmFzdHMoZEMyJG1ldGhvZCkKCnQudGVzdChzY29yZSB+IG1ldGhvZCwgdmFyLmVxdWFsID0gVCwgZGF0YSA9IGRDMikKYGBgCgoKIyMgZW1tZWFucyAKCkJlZm9yZSwgSSBzYWlkIHRoYXQgdGhlIHNtYWxsZXN0IG51bWJlciBvZiBsaW5lcyB3ZSBuZWVkIHRvIGNvbXBhcmUgdGhyZWUgZ3JvdXBzIGlzIHR3by4KQnV0IHRoaXMgYWN0dWFsbHkgbWVhbnMgdGhhdCBvbmUgb2YgdGhlIHBvc3NpYmxlIGNvbXBhcmlzb25zIGlzIGxlZnQgb3V0LgoKKD8/PyBhYm92ZSB0aGUgbWlzc2luZyBsaW5lIGJldHdlZW4gc2VsZi10ZXN0IGFuZCBzdW1tYXJpc2UpCgpPcHRpb25zOgoKLSBtYWtlIHNvbWV0aGluZyBlbHNlIHJlZiBsZXZlbC4gVGhlbiB3ZSBnZXQgdGhhdCBjb21wYXJpc29uLiBCdXQgdGhlbiB3ZSdyZSBtaXNzaW5nIGEgZGlmZmVyZW50IHRoaXJkIGNvbXBhcmlzb24uCi0gZ2V0IGVtbWVhbnMsIGFuZCBvbmNlIHdlJ3JlIGluIHRoZSBvdXRjb21lIHNwYWNlLCB3ZSBjYW4gY29tcHV0ZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHdoYXRldmVyIHdlIHdhbnQhIHlheSEKCgpgYGB7cn0KKG1vZDJfZW1tIDwtIGVtbWVhbnMobW9kMiwgfm1ldGhvZCkpCmBgYAoKCmBgYHtyfQptb2QyX2VtbSB8PiBwbG90KCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCgojIyMgb3ZlcmxheSBlbW1lYW5zIG9uIGRhdGEgcGxvdAoKYGBge3J9Cm1vZDJfZW1tX3RpYiA8LSBtb2QyX2VtbSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgcmVuYW1lKHNjb3JlID0gZW1tZWFuKQpgYGAKCmBgYHtyfQp0ZXN0X3N0dWR5MyB8PgogIGdncGxvdChhZXMoeCA9IG1ldGhvZCwgeSA9IHNjb3JlLCBmaWxsID0gbWV0aG9kLCBjb2xvdXIgPSBtZXRob2QpKSArCiAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjUpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBtb2QyX2VtbV90aWIsIGFlcyh5bWluID0gbG93ZXIuQ0wsIHltYXggPSB1cHBlci5DTCksIGNvbG91ciA9ICdibGFjaycsIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbW9kMl9lbW1fdGliLCBjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMykgKwogIE5VTEwKYGBgCgoKIyMjIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpgYGB7cn0KbGV2ZWxzKHRlc3Rfc3R1ZHkzJG1ldGhvZCkKYGBgCgoKQXNzaWduIDEgdG8gdGhlIGxldmVsIHdlIGFyZSBpbnRlcmVzdGVkIGluLCBhbmQgLTEgdG8gdGhlIGJhc2VsaW5lIHdlIHdhbnQgdG8gY29tcGFyZSBpdCB0by4KCmBgYHtyfQptb2QyX2NvbXBhcmlzb25zIDwtIGxpc3QoCiAgJ1NlbGYtdGVzdCB2cy4gUmVhZCcgPSBjKC0xLCAxLCAwKSwgICMgcmVhZCBzZWxmLXRlc3Qgc3VtbWFyaXNlLCBpbiB0aGF0IG9yZGVyCiAgJ1N1bW1hcmlzZSB2cy4gUmVhZCcgPSBjKC0xLCAwLCAxKSwKICAnU2VsZi10ZXN0IHZzLiBTdW1tYXJpc2UnID0gYygwLCAxLCAtMSksCiAgJ21lYW4oU2VsZi10ZXN0LCBzdW1tYXJpc2UpIHZzLiBSZWFkJyA9IGMoLTEsIDAuNSwgMC41KSAgIyB3ZWlnaHRzIG11c3Qgc3VtIHRvIDAgLS0gcG9vbHMgc2VsZi10ZXN0IGFuZCBzdW1tYXJpc2UgdG9nIGFuZCBwaXRzIHRoYXQgbWVhbiBhZ2FpbnN0IG1lYW4gb2YgUmVhZAopCmBgYAoKCjEgLSAxIAoKYG1lYW4oU2VsZi10ZXN0KSAtIG1lYW4oUmVhZClgID0gcG9zaXRpdmUsIGJjIHNlbGYtdGVzdCBpcyBsYXJnZXIgdGhhbiByZWFkCgpgYGB7cn0KKG1vZDJfY29tcGFyaXNvbnNfdGVzdCA8LSBjb250cmFzdChtb2QyX2VtbSwgbW9kMl9jb21wYXJpc29ucykpCmBgYAoKYGNvbnRyYXN0KClgIGdpdmVzIHVzIHRoZSBwLXZhbHVlcyBmb3IgZWFjaCBkaWZmZXJlbmNlLgpXZSBjYW4gYWxzbyBnZXQgdGhlIDk1JSBDSXMgb2YgZWFjaCBkaWZmZXJlbmNlIGJ5IGdpdmluZyB0aGUgb3V0Y29tZSBvZiBgY29udHJhc3QoKWAgaW50byBgY29uZmludCgpYC4KCmBgYHtyfQpjb25maW50KG1vZDJfY29tcGFyaXNvbnNfdGVzdCkKYGBgCgoKCg==